#include "record.h"
#include "table.h"
#include "tablemanager.h"
#include "dbms.h"
#include <iostream>
#include <cstring>

/*void testTable() {
    cout << "===TEST TABLE===" << endl;
    Table table;
    table.setExpression("customer(id int(10) NOT NULL, name varchar(25) NOT NULL, gender varchar(1) NOT NULL, PRIMARY KEY(id))");
    vector<unsigned int> data = table.getData();
    table.setData(data);
    table.output();

    //EXPECTED OUTPUT:
    //===TEST TABLE===
    //TABLE custome
    //PRIMARY KEY(id)
    //id int(10) NOT NULL
    //name varchar(25) NOT NULL
    //gender varchar(1) NOT NULL
}

void testRecord() {
    cout << "===TEST RECORD===" << endl;
    Table table;
    table.setExpression("student(id int(10) NOT NULL, name varchar(25) NOT NULL)");
    Record record(table);
    record.setExpression("(2013011350, 'zsyzgu')");
    record.output();

    //EXPECTED OUTPUT:
    //===TEST RECORD===
    //id : 2013011350
    //name : zsyzgu
}

void testTableManager() {
    cout << "===TEST TABLE_MANAGER===" << endl;
    DBMS dbms;
    dbms.createDatabase("test.db");
    Table table;
    table.setExpression("student(id int(10) NOT NULL, name varchar(25) NOT NULL)");
    TableManager tableManager("test.db");
    tableManager.createTable(table);
    tableManager.openTable("student");

    Record record(table);
    record.setExpression("(2013011350, 'zsyzgu')");
    tableManager.insertRecord(record);
    record.setExpression("(2013011332, 'lufeng')");
    tableManager.insertRecord(record);

    if (tableManager.queryRecord("name", "zsyzgu", record)) {
        cout << record.find("id") << endl;
    }
    if (tableManager.queryRecord("id", "2013011350", record)) {
        cout << record.find("name") << endl;
    }
    if (tableManager.queryRecord("id", "2013011332", record)) {
        cout << record.find("id") << endl;
    }

    tableManager.closeTable();
    tableManager.dropTable("student");
    dbms.dropDatabase("test.db");

    //EXPECTED OUTPUT:
    //===TEST TABLE_MANAGER===
    //2013011350
    //zsyzgu
    //2013011332
}

void testDBMS() {
    cout << "===TEST DBMS===" << endl;
    DBMS dbms;
    dbms.createDatabase("test.db");
    dbms.useDatabase("test.db");

    Table table;
    table.setExpression("student(id int(10) NOT NULL, name varchar(25) NOT NULL)");
    dbms.createTable(table);
    table.setExpression("customer(id int(10) NOT NULL, name varchar(25) NOT NULL, gender varchar(1) NOT NULL, PRIMARY KEY(id))");
    dbms.createTable(table);

    dbms.showDatabase();
    dbms.dropDatabase("test.db");

    //EXPECTED OUTPUT:
    //===TEST DBMS===
    //test.db
    //  customer
    //  student
}*/

void test() {
    // ----- SYSTEM -----
    // [S1] CREATE DATABASE test.db;
    // [S2] DROP DATABASE test.db;
    // [S3] USE test.db;
    // [S4] SHOW TABLES;
    // [S5] CREATE TABLE customer(id int(10) NOT NULL, name varchar(25), PRIMARY KEY(id));
    // [S6] DROP TABLE customer;
    // [S7] DESC customer;
    // ----- QUERY -----
    // [Q1] INSERT INTO customer VALUES(1000, 'State', 'F');
    // [Q2] DELETE FROM publisher WHERE state = 'CA';
    // [Q3] UPDATE book SET title = 'Handsome' WHERE authors = 'zsyzgu';
    // [Q4] SELECT title FROM book WHERE authors is null;
    // [Q5] SELECT * FROM publisher WHERE state = 'CA';
    // [Q6] SELECT book.title, orders.quality FROM book, orders WHERE book.id = orders.book_id AND orders.quality > 8;

    DBMS dbms;

    // [S1] CREATE DATABASE test.db
    // The name of dasebase must end with ".db" now, because it's a directory and should be different with others.
    dbms.createDatabase("test.db");

    // [S3] USE test.db
    // State using the dasebase before using it.
    dbms.useDatabase("test.db");

    // [S5] CREATE TABLE student(id int(10) NOT NULL, name varchar(25), PRIMARY KEY(id));
    // The input is table expression.
    dbms.createTable("student(id int(10) NOT NULL, name varchar(25), PRIMARY KEY(id))");

    // [Q1] INSERT INTO student VALUES(2013011350, 'zsyzgu');
    // Sometimes this command have more then one records, for example "INSERT INTO table value1, value2, value3;"
    dbms.insertRecord("student", "(2013011350, 'zsyzgu')");

    // [EX] INSERT INTO student VALUES(2013011343, NULL);
    dbms.insertRecord("student", "(2013011343, NULL)");

    // [Q4] SELECT id FROM student WHERE name is null;
    // "NULL" is a special string to represent a null value, I wish there is no data named "NULL".
    // <Expected output> $2013011343
    vector<Record> records = dbms.getRecords("student");
    for (int i = 0; i < records.size(); i++) {
        if (records[i].find("name") == "NULL") {
            cout << records[i].find("id") << endl;
        }
    }

    // [Q3] UPDATE student SET name = 'hanxu' WHERE id = 2013011343;
    // For each record, the value of "PRIMARY KEY" is the index for UPDATE and DELETE.
    records = dbms.getRecords("student");
    for (int i = 0; i < records.size(); i++) {
        if (records[i].find("id") == "2013011343") {
            records[i].update("name", "hanxu");
            dbms.updateRecord("student", records[i]);
        }
    }

    // [Q2] DELETE FROM student WHERE name = 'zsyzgu';
    // Delete a record according to its value of "PRIMARY KEY"
    records = dbms.getRecords("student");
    for (int i = 0; i < records.size(); i++) {
        if (records[i].find("name") == "zsyzgu") {
            dbms.deleteRecord("student", records[i].getPrimaryValue());
        }
    }

    // [Q5] SELECT * FROM publisher WHERE name = 'hanxu';
    records = dbms.getRecords("student");
    for (int i = 0; i < records.size(); i++) {
        if (records[i].find("name") == "hanxu") {
            records[i].output();
        }
    }

    // [S4] SHOW TABLES;
    // Show all tables of current database.
    // <Expected output> $student
    dbms.showTables();

    // [S7] DESC customer;
    // Show the schema of the table.
    // <Expected output>
    // $TABLE student
    // $    PRIMARY KEY(id)
    // $    id int(10) NOT NULL
    // $    name varchar(25)
    dbms.descTable("student");

    // [S6] DROP TABLE customer;
    dbms.dropTable("student");

    // [S2] DROP DATABASE test.db
    dbms.dropDatabase("test.db");
}

int main() {
    test();

	return 0;
}
